<?php

/**
 * 设置或者获取当前语言
 * 
 * @param string $lang
 * @return string
 */
function lang($lang = '') {
    static $language;
    if ($lang) {
        $language = $lang;
    } else {
        $lang = $language;
        if (!$lang) {
            $lang = 'zh_cn';
            $language = $lang;
        }
        return $lang;
    }
}

/**
 * 按语言设置解析文字
 */
function L($paths, $args = array(), $lang = '') {
    if (!$lang) {
        $lang = lang();
    }

    $ret = include(ROOT_PATH . '/Lang/' . $lang . '.php');

    $keys = explode('/', $paths);
    foreach ($keys as $key) {
        if (!$ret = $ret[$key]) {
            return false;
        }
    }
    if ($args) {
        $ret = vsprintf($ret, $args);
    }
    return $ret;
}

function MEncode($str) {
    return Mcrypt::encode($str, Sys::getCfg('base_secret_key'));
}

function MDecode($str) {
    return Mcrypt::decode($str, Sys::getCfg('base_secret_key'));
}

/**
 * 显示消息带换行和时间
 * 
 * @param $message
 * @return boolean
 */
function eol($message) {
    if ($message === '') {
        return false;
    }
    if (!is_string($message)) {
        $message = print_r($message, true);
    }
    echo "---------------[" . date('Y-m-d H:i:s') . "]---------------" . PHP_EOL . $message . PHP_EOL;
    return true;
}

/**
 * 按行显示html
 * 
 * @param $message
 */
function print_br($message) {
    if ($message === '') {
        return false;
    }
    if (!is_string($message)) {
        $message = print_r($message, true);
    }
    echo $message . '<br />';
    return true;
}

/**
 * 写日志,日志都写在 Data/Logs 目录下
 *
 * @param string $message 日志内容
 * @param string $type 日志类型
 */
function wlog($message, $type = 'user') {
    // 得到日志目录
    $path = DATA_PATH . "/Logs/{$type}/" . date('Y-m');
    if (!file_exists($path)) {
        @mkdir($path, 0775, true);
    }

    if (!is_string($message)) {
        $message = print_r($message, true);
    }

    // 得到日志文件
    $file = $path . '/' . date('Y_m_d') . '.log';
    $msg = "---------------[" . date('Y-m-d H:i:s') . "]---------------" . PHP_EOL . $message . PHP_EOL;
    file_put_contents($file, $msg, FILE_APPEND);
}

/**
 * 数组预处理，以便输出为json时有更好的数据格式
 * 
 * 小数去除右侧的0值
 * null处理为空字符串
 * 
 * @return array
 */
function array_pretreat(&$array) {
    if (is_array($array)) {
        foreach ($array as &$value) {
            if (is_array($value)) {
                array_pretreat($value);
            } else {
                if (is_numeric($value)) {
                    // mysql int 默认11位，excel超过11位也会用科学计数法表示
                    if ($value < 99999999999) {
                        $value = floatval($value);
                    }
                }
                if (is_null($value)) {
                    $value = '';
                }
            }
        }
    }
    return $array;
}

/**
 * 递归解码URL
 * 
 * 该方法不会出现多次解析导致参数部分的url被解码的问题
 */
function http_url_decode($url) {
    if (!$url) {
        return '';
    }

    if (strpos($url, 'https') === 0) {
        $http = 'https';
    } else if (strpos($url, 'http') === 0) {
        $http = 'http';
    } else {
        return urldecode($url);
    }

    if (strpos($url, $http . '://') === 0) {
        return $url;
    } else {
        return http_url_decode(urldecode($url));
    }
}

/**
 * 任意多个变量的调试输出
 *
 * @param mixed [$var1,$var2,$var3,...]
 */
function dump() {
    if (PHP_SAPI != 'cli') {
        header("Content-Type:text/html; charset=" . Sys::getCfg('charset'));
    }
    echo '<pre style="font-size:12px; color:#0000FF">' . PHP_EOL;
    $vars = func_get_args();
    foreach ($vars as $var) {
        if (is_array($var)) {
            print_r($var);
        } else if (is_object($var)) {
            print_r($var);
        } else if (is_resource($var)) {
            echo (string) $var;
        } else {
            echo var_dump($var);
        }
    }
    echo '</pre>' . PHP_EOL;
    exit;
}

/**
 * 返回指间区间内的一个随机数,如果没有指定则返回 0- RAND_MAX 中取一个乱数
 */
function randNum($min = null, $max = null) {
    if ($min === null || $max === null)
        return mt_rand();
    else
        return mt_rand($min, $max);
}

/**
 * 产生随机字串,默认长度6位,字母和数字混合
 *
 * @param string $len 长度
 * @param string $type 随机符类型 0:数字 1:小写字母 2:大写字母 3:所有字母与数字组合
 * @param string $addChars 额外字符
 * @return string
 */
function randString($len = 6, $type = 3, $addChars = '') {
    $str = '';
    switch ($type) {
        case 0:
            $chars = str_repeat('0123456789', 3) . $addChars;
            break;
        case 1:
            $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars;
            break;
        case 2:
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars;
            break;
        case 3:
            //去掉了容易混淆的字符oOLl和数字01
            $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars;
            break;
    }
    //位数过长重复字符串一定次数
    if ($len > 10) {
        $chars = $type == 0 ? str_repeat($chars, $len) : str_repeat($chars, 5);
    }
    //得到随机数
    $charLen = strlen($chars) - 1;
    for ($i = 0; $i < $len; $i++) {
        $n = randNum(0, $charLen);
        $str .= $chars[$n];
    }
    return $str;
}

/**
 * 计算中英文混合字符串的长度
 * 
 * @param string $str
 * @return int
 */
function cstrlen($str) {
    return strlen(preg_replace('/\s/', '', preg_replace('/[\x80-\xff]{1,3}/', 'a', $str)));
}

/**
 * 字符串截取，支持中文和其他编码
 *
 * @param string $str 需要转换的字符串
 * @param string $start 开始位置
 * @param string $length 截取长度
 * @param string $charset 编码格式
 * @param string $suffix 截断显示字符,是否显示 '...'
 * @return string
 */
function msubstr($str, $start = 0, $length, $charset = "utf-8", $suffix = true) {
    if (function_exists("mb_substr")) {
        $strlen = mb_strlen($str, $charset);
        $slice = mb_substr($str, $start, $length, $charset);
    } elseif (function_exists('iconv_substr')) {
        $strlen = iconv_strlen($str, $charset);
        $slice = iconv_substr($str, $start, $length, $charset);
    } else {
        $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $strlen = strlen($str);
        $slice = join("", array_slice($match[0], $start, $length));
    }
    if ($suffix && $strlen > $length)
        return $slice . "…";
    return $slice;
}

/**
 * 转换 HTML 特殊字符以及空格和换行符
 *
 * 一般将 <textarea> 标记中输入的内容从数据库中读出来后在网页中显示
 */
function toHtml($text) {
    $text = htmlspecialchars($text);
    $text = nl2br(str_replace(' ', '&nbsp;', $text));
    return $text;
}

/**
 * 过滤所有的JS，STYLE，HTML标记
 *
 * @param unknown_type $string
 * @return unknown
 */
function stripTag($string) {
    $string = preg_replace("/<script.*>.*<\/script>/isU", "", $string);
    $string = preg_replace("/<style.*>.*<\/style>/isU", "", $string);
    $string = strip_tags($string);
    return $string;
}

/**
 * 返回永远唯一32位md5的随机数
 */
function md5Rand() {
    return md5(uniqid(time() . rand()));
}

/**
 * 得到小写的文件扩展名,如".php"
 *
 * @param string $fileName:文件名
 * @return string
 */
function fileExt($fileName) {
    return strtolower(strrchr($fileName, "."));
}

/**
 * 获取当前毫秒数
 * @return float
 */
function millisecond() {
    list($msec, $sec) = explode(' ', microtime());
    return (float) sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
}

/**
 * 缓存函数
 * @param type $type 缓存分类
 * @param type $key 缓存键
 * @param type $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param type $expire 缓存过期时间
 * @param type $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function cache($type, $key, $data = null, $expire = 0, $remove = false) {
    $default_cache_type = Sys::getCfg('default_cache_type');
    if (!$default_cache_type) {
        $default_cache_type = 'file';
    }
    switch ($default_cache_type) {
        case 'file':
            return fcache($type, $key, $data, $expire, $remove);
            break;
        case 'apcu':
            return ucache($type, $key, $data, $expire, $remove);
            break;
        case 'redis':
            return rcache($type, $key, $data, $expire, $remove);
            break;
    }
    throw new Exception('未配置 default_cache_type');
}

/**
 * 文件缓存
 * @param type $dir 缓存目录
 * @param type $key 缓存键
 * @param type $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param type $expire 缓存过期时间
 * @param type $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function fcache($dir, $key, $data = null, $expire = 0, $remove = false) {

    static $cache;

    if (!$cache) {

        $cache = new Cache();
    }

    $dir = DATA_PATH . "/Cache/{$dir}/";

    if (!file_exists($dir)) {
        mkdir($dir, 0775, true);
    }

    $cache->setPath($dir);

    if ($remove) {
        return $cache->remove($key);
    }

    if ($data !== null) {
        return $cache->set($key, $data, $expire);
    } else {
        return $cache->get($key);
    }
}

/**
 * APCU缓存
 * @param type $type 缓存分类
 * @param type $key 缓存键
 * @param type $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param type $expire 缓存过期时间
 * @param type $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function ucache($type, $key, $data = null, $expire = 0, $remove = false) {

    static $apcu;
    static $config;

    $config || $config = Sys::getCfg('apcu');

    if (!$apcu) {

        $apcu = new Apcu();
    }

    if ($config['prefix']) {
        $key = $config['prefix'] . $type . '_' . $key;
    } else {
        $key = $type . '_' . $key;
    }

    if ($remove) {
        return $apcu->remove($key);
    }

    if ($data !== null) {
        return $apcu->set($key, $data, $expire);
    } else {
        return $apcu->get($key);
    }
}

/**
 * redis缓存
 * @param type $type 缓存分类
 * @param type $key 缓存键
 * @param type $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param type $expire 缓存过期时间
 * @param type $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function rcache($type, $key, $data = null, $expire = 0, $remove = false) {
    static $config;

    $config || $config = Sys::getCfg();

    $rdconfig = $config['redis']['master'];

    $type = strtolower($type);
    $key = strtolower($key);

    if ($rdconfig['prefix']) {
        $key = $rdconfig['prefix'] . $type . '_' . $key;
    } else {
        $key = $type . '_' . $key;
    }

    if ($remove) {
        try {
            Rd()->del($key);
        } catch (Exception $ex) {
            return false;
        }
        return true;
    }

    if ($data !== null) {
        $data = serialize($data);
        try {
            if ($expire == 0) {
                Rd()->set($key, $data);
            } else {
                Rd()->setex($key, $expire, $data);
            }
        } catch (Exception $ex) {
            return false;
        }

        return true;
    } else {
        try {
            $data = Rd()->get($key);
        } catch (Exception $ex) {
            return false;
        }

        if (!$data) {
            return false;
        } else {
            return unserialize($data);
        }
    }
}

/**
 * Redis HashTable 缓存
 * @param string $key
 * @param string $hashKey
 * @param mixed $value
 * @param int $remove 0 不删除，1 删除hashKey和value，2 删除key对应的整个hashtable
 * @return mixed
 */
function hcache($key, $hashKey, $value = null, $remove = 0) {
    static $config;

    $config || $config = Sys::getCfg();

    $rdconfig = $config['redis']['master'];

    $key = strtolower($key);

    if ($rdconfig['prefix']) {
        $key = $rdconfig['prefix'] . $key;
    }

    if ($remove == 2) {
        try {
            Rd()->del($key);
        } catch (Exception $ex) {
            return false;
        }
        return true;
    }

    $hashKey = strtolower($hashKey);

    if ($remove == 1) {
        try {
            Rd()->hDel($key, $hashKey);
        } catch (Exception $ex) {
            return false;
        }
        return true;
    }

    if ($value !== null) {

        $value = serialize($value);

        try {
            Rd()->hSet($key, $hashKey, $value);
        } catch (Exception $ex) {
            return false;
        }

        return true;
    } else {

        try {
            $value = Rd()->hGet($key, $hashKey);
        } catch (Exception $ex) {
            return null;
        }

        if (!$value) {
            return false;
        } else {
            return unserialize($value);
        }
    }
}

/**
 * 数据操作类
 * @return Mysql
 */
function Db($server = 'master') {

    static $db = array();
    static $config;

    $config || $config = Sys::getCfg();

    $dbconfig = $config['database'][$server];
    $dbconfig['debug'] = $config['debug'];
    $dbconfig['display_error'] = $config['display_error'];

    if (!isset($db[$server])) {
        $db[$server] = new Mysql($dbconfig);
    }

    return $db[$server];
}

/**
 * Redis操作类
 * @return Redis
 */
function Rd($server = 'master') {

    static $rd = array();
    static $config;

    $config || $config = Sys::getCfg();

    $rdconfig = $config['redis'][$server];

    if (!isset($rd[$server])) {
        try {
            $rd[$server] = new Redis();
        } catch (Exception $ex) {
            $rd[$server] = null;
        }
    }

    $connected = false;

    try {
        if ($rd[$server]->ping()) {
            $connected = true;
        }
    } catch (Exception $ex) {
        
    }

    if (!$connected) {
        $rd[$server]->pconnect($rdconfig['host'], $rdconfig['port']);
        if ($rdconfig['password']) {
            $rd[$server]->auth($rdconfig['password']);
        }
    }

    return $rd[$server];
}

/**
 * 返回客户端IP
 * 
 * @return string
 */
function clientip() {
    if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
        $ip = getenv("HTTP_CLIENT_IP");
    else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
        $ip = getenv("HTTP_X_FORWARDED_FOR");
    else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
        $ip = getenv("REMOTE_ADDR");
    else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
        $ip = $_SERVER['REMOTE_ADDR'];
    else
        $ip = "unknown";
    return($ip);
}

/**
 * 写入token，获取token对应的用户调用 decode_token 函数
 * 
 * @param int $expire 过期时间，秒
 * @return string 返回token，释放返回 false
 */
function refresh_token($uid, $type = 'user', $prefix = '', $expire = 1440) {
    $user['uid'] = $uid;
    $user['type'] = $type;
    $token = MEncode(randString() . '_' . $uid);
    $user['token'] = $token;
    if (cache("{$prefix}token_{$type}", $uid, $user, $expire)) {
        return $token;
    }
}

/**
 * 获取用户的token
 */
function read_token($uid, $type = 'user', $prefix = '') {
    $user = cache("{$prefix}token_{$type}", $uid);
    if (!$user || !$user['token']) {
        return false;
    }
    return $user['token'];
}

function remove_token($uid, $type = 'user', $prefix = '') {
    return cache("{$prefix}token_{$type}", $uid, '');
}

/**
 * 获取token对应的用户信息
 */
function decode_token($token, $type = 'user', $prefix = '') {
    $decode_token = MDecode($token);
    if (!$decode_token) {
        return false;
    }
    list($rnd, $uid) = explode('_', $decode_token);
    if (!$uid) {
        return false;
    }
    $user = cache("{$prefix}token_{$type}", $uid);
    if (!$user || $user['token'] != $token) {
        return false;
    }
    return $user;
}

/**
 * 过滤utf8mb4字符
 */
function filter_utf8mb4($str) {
    return preg_replace_callback('/./u', function (array $match) {
        return strlen($match[0]) >= 4 ? '' : $match[0];
    }, $str);
}
